package lua.compiler.tokens;
import lua.compiler.CompilerException;
import lua.compiler.CtsType;
import lua.compiler.Function;
import lua.compiler.Functions;

import org.antlr.runtime.tree.Tree;

public class CallToken extends GenericToken {
	// funkcja print jest specjalnie obslugiwana, dzieki temu moze obslugiwac
	// dowolna ilosc argumentow
	void parsePrint(Tree tree, Function function) throws CompilerException {
		for (int i = 1; i < tree.getChildCount(); i++)
		{	
			Tree child = tree.getChild(i);
			String param = child.toString();
			int tokenType = child.getType();
			String token = Tokens.getToken(tokenType);
			
			if (token.equals("STRING"))	{
				function.addInstruction("ldstr " + param);
				function.addInstruction("call void [mscorlib]System.Console::Write(string)");
			}
			else if (token.equals("INT")) {
				function.addInstruction("ldc.i4 " + param);
				function.addInstruction("call void [mscorlib]System.Console::Write(int32)");
			}
			else if (token.equals("FLOAT")) {
				function.addInstruction("ldc.r4 " + param);
				function.addInstruction("call void [mscorlib]System.Console::Write(float32)");
			}
			else if (token.equals("CALL")) {
				String calledFunctionName = child.getChild(0).getText();
				Function calledFunction = Functions.getFunction(calledFunctionName);
				
				if (calledFunction == null) {
					throw new CompilerException(
						tree, "Funkcja " + calledFunctionName + " nie jest zdefiniowana");
				}
				CtsType functionType = calledFunction.getReturnType();
				
				if (functionType == CtsType.VOID) {
					throw new CompilerException(
						tree, "Nie mozna wypisywac wartosci typu void");
				}
				// jesli jest call print(call fun(...)), musimy wyewaluowac wewnetrzna funkcje 
				parse(tree.getChild(i), function);

				function.addInstruction("call void [mscorlib]System.Console::Write(" + functionType + ")");
			}
			// wypisujemy jakis expression - ewaluujemy go i wolamy Write
			else {
				ExpToken expToken = new ExpToken();
				expToken.parse(child, function);
				
				CtsType expType = expToken.getCtsType(); 

				function.addInstruction("call void [mscorlib]System.Console::Write("+ expType + ")");
			}
		}
	}
	
	@Override
	public void parseImpl(Tree tree, Function function) throws CompilerException {
		String funcName = tree.getChild(0).getText();
		
		StringBuilder callString;
		
		if (funcName.equals("print")) {
			parsePrint(tree, function);
			return;
		}
		
		Function calledFunction = Functions.getFunction(funcName);
		callString = new StringBuilder("call " + calledFunction.getReturnType() + " ");
			 
		callString.append(" Main::" + funcName + "(");
		
		CtsType[] paramsTypes = calledFunction.getParamsTypes();		
		for (int i = 1; i < tree.getChildCount(); i++)
		{
			if (i > 1)
				callString.append(", ");
			
			CtsType paramCtsType = paramsTypes[i-1];
			callString.append(paramCtsType);
			
			Tree child = tree.getChild(i);
			
			StmtToken stmtToken = new StmtToken();
			stmtToken.parse(child, function);
			CtsType stmtType = stmtToken.getCtsType();
			
			stmtType.ConvertTo(paramCtsType, function);
		}

		callString.append(")");
		function.addInstruction(callString.toString());
		this.setCtsType(calledFunction.getReturnType());
	}

}
